//
//  UUAlertViewController.m
//  UUZuche
//
//  Created by phenmod on 14-10-16.
//
//

#import "JNAlertController.h"
#import <objc/runtime.h>
#import "UUSingleAlertController.h"

static const char UUAlertActionKey;
static const char RetainSelfKey;
static void *UUWeakAlertViewKey = "UUWeakAlertViewKey";


@interface JNAlertController () <UIAlertViewDelegate, UIActionSheetDelegate>
@property (nonatomic, strong) NSMutableArray *_actions;
@property (nonatomic, strong) UUAlertAction *cancelAction;
@property (nonatomic, strong) UUAlertAction *destructiveAction;
@property (nonatomic, assign) UUAlertControllerStyle preferredStyle;
@end


@implementation JNAlertController
@dynamic actions;

// =================================================================================================
#pragma mark - Utility
// =================================================================================================

// 创建单例AlertView
- (id)initSingleAlertControllerWithTitle:(NSString *)title
                                 message:(NSString *)message
                          preferredStyle:(UUAlertControllerStyle)preferredStyle
                                 actions:(NSArray *)actions
{
    if ((self = [JNAlertController alertControllerWithTitle:title
                                                    message:message
                                             preferredStyle:preferredStyle
                                                    actions:actions]) != nil)
    {
        [[UUSingleAlertController getInstance] showNewAlertView:self];
    }

    return self;
}

// 常用单按钮AlertView
+ (instancetype)alertOneActionWithTitle:(NSString *)title
                                message:(NSString *)message
                            actionTitle:(NSString *)actionTitle
                                 action:(UUAlertActionHandlerBlock)actionHandler
{
    JNAlertController *alertController =
        [JNAlertController alertControllerWithTitle:title
                                            message:message
                                     preferredStyle:UUAlertControllerStyleAlert];
    [alertController addActionWithTitle:actionTitle
                                  style:UUAlertActionStyleCancel
                                handler:actionHandler];
    return alertController;
}

// 常用双按钮AlertView，左侧取消无动作，右侧有动作
+ (instancetype)alertTwoActionsWithTitle:(NSString *)title
                                 message:(NSString *)message
                             cancelTitle:(NSString *)cancelTitle
                             actionTitle:(NSString *)actionTitle
                                  action:(UUAlertActionHandlerBlock)actionHandler
{
    JNAlertController *alertController =
        [JNAlertController alertControllerWithTitle:title
                                            message:message
                                     preferredStyle:UUAlertControllerStyleAlert];
    [alertController addActionWithTitle:cancelTitle
                                  style:UUAlertActionStyleDefault
                                handler:^(UUAlertAction *action){
                                }];
    [alertController addActionWithTitle:actionTitle
                                  style:UUAlertActionStyleDefault
                                handler:actionHandler];
    return alertController;
}

// 常用双按钮AlertView，左侧取消有动作，右侧有动作
+ (instancetype)alertTwoActionsWithTitle:(NSString *)title
                                 message:(NSString *)message
                             cancelTitle:(NSString *)cancelTitle
                            cancelAction:(UUAlertActionHandlerBlock)cancelActionHandler
                             actionTitle:(NSString *)actionTitle
                                  action:(UUAlertActionHandlerBlock)actionHandler
{
    JNAlertController *alertController =
        [JNAlertController alertControllerWithTitle:title
                                            message:message
                                     preferredStyle:UUAlertControllerStyleAlert];
    [alertController addActionWithTitle:cancelTitle
                                  style:UUAlertActionStyleDefault
                                handler:cancelActionHandler];
    [alertController addActionWithTitle:actionTitle
                                  style:UUAlertActionStyleDefault
                                handler:actionHandler];
    return alertController;
}

// =================================================================================================
#pragma mark - Initialisation
// =================================================================================================

+ (instancetype)alertControllerWithTitle:(NSString *)title
                                 message:(NSString *)message
                          preferredStyle:(UUAlertControllerStyle)preferredStyle
{
    return [self alertControllerWithTitle:title
                                  message:message
                           preferredStyle:preferredStyle
                                  actions:nil];
}

+ (instancetype)alertControllerWithTitle:(NSString *)title
                                 message:(NSString *)message
                          preferredStyle:(UUAlertControllerStyle)preferredStyle
                                 actions:(NSArray *)actions
{
    // 适配iOS8的alert样式
    if (!title)
    {
        // 如果title==nil，message上部比较贴近alert顶端，不好看。
        title = @"";
    }
    else if (title.length)
    {
        // 如果title.length != 0, message比较贴近title，不好看。
        message = [NSString stringWithFormat:@"\n%@", message];
    }

    JNAlertController *controller = [JNAlertController new];
    controller.title = title;
    controller.message = message;
    controller.preferredStyle = preferredStyle;
    controller._actions = [NSMutableArray array];
    for (UUAlertAction *action in actions)
    {
        [controller addAction:action];
    }
    return controller;
}

- (NSArray *)actions
{
    return self._actions;
}

- (void)addActionWithTitle:(NSString *)title
                     style:(UUAlertActionStyle)style
                   handler:(UUAlertActionHandlerBlock)handler
{
    [self addAction:[UUAlertAction actionWithTitle:title style:style handler:handler]];
}

- (void)addAction:(UUAlertAction *)action
{
    NSAssert(action, @"Action cannot be nil");
    NSAssert(!(self.preferredStyle == UUAlertControllerStyleAlert
               && action.style == UUAlertActionStyleDestructive),
             @"Alert cannot have a destructive action");
    NSAssert(!(action.style == UUAlertActionStyleCancel && self.cancelAction != nil),
             @"Can only have 1 Cancel action");
    NSAssert(!(action.style == UUAlertActionStyleDestructive && self.destructiveAction != nil),
             @"Can only have 1 Destructive action");
    switch (action.style)
    {
    case UUAlertActionStyleCancel:
        self.cancelAction = action;
        break;
    case UUAlertActionStyleDestructive:
        self.destructiveAction = action;
        break;
    default:
        [self._actions addObject:action];
        break;
    }
}

- (BOOL)isAlertControllerAvailable
{
    return NSClassFromString(@"UIAlertController") != nil;
}

// =================================================================================================
#pragma mark - UI objects creation
// =================================================================================================

- (UIAlertView *)alertView
{
    UIAlertView *alertView = objc_getAssociatedObject(self, UUWeakAlertViewKey);
    if (alertView)
    {
        return alertView;
    }

    alertView = [[UIAlertView alloc] initWithTitle:self.title
                                           message:self.message
                                          delegate:self
                                 cancelButtonTitle:self.cancelAction.title
                                 otherButtonTitles:nil];
    for (UUAlertAction *action in self._actions)
    {
        NSInteger index = [alertView addButtonWithTitle:action.title];
        action.assignedIndex = index;
    }
    objc_setAssociatedObject(alertView, &RetainSelfKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    objc_setAssociatedObject(self, UUWeakAlertViewKey, alertView, OBJC_ASSOCIATION_ASSIGN);

    return alertView;
}

- (UIActionSheet *)actionSheet
{
    UIActionSheet *actionSheet = objc_getAssociatedObject(self, UUWeakAlertViewKey);
    if (actionSheet)
    {
        return actionSheet;
    }

    actionSheet = [[UIActionSheet alloc] initWithTitle:self.title
                                              delegate:self
                                     cancelButtonTitle:nil
                                destructiveButtonTitle:nil
                                     otherButtonTitles:nil];
    for (UUAlertAction *action in self._actions)
    {
        NSInteger index = [actionSheet addButtonWithTitle:action.title];
        action.assignedIndex = index;
    }
    if (self.destructiveAction)
    {
        actionSheet.destructiveButtonIndex =
            [actionSheet addButtonWithTitle:self.destructiveAction.title];
    }
    if (self.cancelAction)
    {
        actionSheet.cancelButtonIndex = [actionSheet addButtonWithTitle:self.cancelAction.title];
    }
    objc_setAssociatedObject(actionSheet, &RetainSelfKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    objc_setAssociatedObject(self, UUWeakAlertViewKey, actionSheet, OBJC_ASSOCIATION_ASSIGN);

    return actionSheet;
}

- (UIAlertController *)alertController
{
    UIAlertController *alertController = objc_getAssociatedObject(self, UUWeakAlertViewKey);
    if (alertController)
    {
        return alertController;
    }

    alertController =
        [UIAlertController alertControllerWithTitle:self.title
                                            message:self.message
                                     preferredStyle:(UIAlertControllerStyle)self.preferredStyle];
    if (self.cancelAction)
    {
        [self._actions addObject:self.cancelAction];
    }
    if (self.destructiveAction)
    {
        [self._actions addObject:self.destructiveAction];
    }
    for (UUAlertAction *IDaction in self._actions)
    {
        UIAlertAction *action =
            [UIAlertAction actionWithTitle:IDaction.title
                                     style:(UIAlertActionStyle)IDaction.style
                                   handler:^(UIAlertAction *action) {
                                       UUAlertAction *underlyinAction
                                           = objc_getAssociatedObject(action, &UUAlertActionKey);
                                       if (underlyinAction)
                                       {
                                           if (underlyinAction.handlerBlock)
                                           {
                                               underlyinAction.handlerBlock(underlyinAction);
                                           }
                                           objc_setAssociatedObject(
                                               action, &UUAlertActionKey, nil,
                                               OBJC_ASSOCIATION_RETAIN_NONATOMIC);
                                           objc_setAssociatedObject(self, UUWeakAlertViewKey, nil,
                                                                    OBJC_ASSOCIATION_ASSIGN);
                                       }
                                   }];
        objc_setAssociatedObject(action, &UUAlertActionKey, IDaction,
                                 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        [alertController addAction:action];
    }

    objc_setAssociatedObject(self, UUWeakAlertViewKey, alertController, OBJC_ASSOCIATION_ASSIGN);

    return alertController;
}

// =================================================================================================
#pragma mark - UIAlertView & UIActionSheet delegate
// =================================================================================================

- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
{
    [self performActionWithButtonIndex:buttonIndex
                     cancelButtonIndex:alertView.cancelButtonIndex
                destructiveButtonIndex:NSNotFound
                                sender:alertView];
}

- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex
{
    [self performActionWithButtonIndex:buttonIndex
                     cancelButtonIndex:actionSheet.cancelButtonIndex
                destructiveButtonIndex:actionSheet.destructiveButtonIndex
                                sender:actionSheet];
}

- (void)performActionWithButtonIndex:(NSInteger)buttonIndex
                   cancelButtonIndex:(NSInteger)cancelButtonIndex
              destructiveButtonIndex:(NSInteger)destructiveIndexButton
                              sender:(id)sender
{
    UUAlertAction *action;
    if (buttonIndex == cancelButtonIndex)
    {
        action = self.cancelAction;
    }
    else if (buttonIndex == destructiveIndexButton)
    {
        action = self.destructiveAction;
    }
    else
    {
        for (UUAlertAction *otherAction in self.actions)
        {
            if (otherAction.assignedIndex == buttonIndex)
            {
                action = otherAction;
                break;
            }
        }
    }
    if (action.handlerBlock)
    {
        action.handlerBlock(action);
    }
}

// =================================================================================================
#pragma mark - Display & dismiss
// =================================================================================================

- (void)show
{
    if (![self isAlertControllerAvailable] && self.preferredStyle == UUAlertControllerStyleAlert)
    {
        // display alertView without resolving
        [[self alertView] show];
    }
    else
    {
        [self showInViewController:[self topViewController]];
    }
}

- (void)showInViewController:(UIViewController *)viewController
{
    [self showInViewController:viewController completion:nil];
}

- (void)showInViewController:(UIViewController *)viewController
                  completion:(void (^)(void))completion
{
    if ([self isAlertControllerAvailable])
    {
        [viewController presentViewController:[self alertController]
                                     animated:YES
                                   completion:completion];
    }
    else
    {
        if (self.preferredStyle == UUAlertControllerStyleAlert)
        {
            [[self alertView] show];
        }
        else
        {
            [[self actionSheet] showInView:viewController.view];
        }
        if (completion)
        {
            completion();
        }
    }
}

- (void)dismiss
{
    id uuAlert = objc_getAssociatedObject(self, UUWeakAlertViewKey);
    if (!uuAlert)
    {
        return;
    }
    if ([self isAlertControllerAvailable])
    {
        [(UIAlertController *)uuAlert dismissViewControllerAnimated:NO completion:nil];
    }
    else
    {
        if (self.preferredStyle == UUAlertControllerStyleAlert)
        {
            [(UIAlertView *)uuAlert dismissWithClickedButtonIndex:0 animated:NO];
        }
        else
        {
            [(UIActionSheet *)uuAlert dismissWithClickedButtonIndex:0 animated:NO];
        }
    }
    objc_removeAssociatedObjects(self);
}

// =================================================================================================
#pragma mark - View controller utils
// =================================================================================================

// top view controller utils
// credit: http://stackoverflow.com/a/17578272/2822938
- (UIViewController *)topViewController
{
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication]
                                                             .keyWindow.rootViewController];
}

- (UIViewController *)topViewControllerWithRootViewController:(UIViewController *)rootViewController
{
    if ([rootViewController isKindOfClass:[UITabBarController class]])
    {
        UITabBarController *tabBarController = (UITabBarController *)rootViewController;
        return
            [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    }
    else if ([rootViewController isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *navigationController = (UINavigationController *)rootViewController;
        return [self
            topViewControllerWithRootViewController:navigationController.visibleViewController];
    }
    else if (rootViewController.presentedViewController)
    {
        UIViewController *presentedViewController = rootViewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    }
    else
    {
        return rootViewController;
    }
}

@end
